#include "stdafx.h"
#include <unistd.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <asm/byteorder.h>
#include <linux/netfilter.h>
#include <libnetfilter_queue/libnetfilter_queue.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <sys/socket.h>
#include "checksum.h"
#include "ip_pack_queue.h"
#include <pthread.h>
#include "thread_context.h"
#include "checkurl.h"

typedef struct nf_context
{
	struct nfq_handle*      handle;
	int						fd;
	int						outeth;
	int						sendfd;
} nf_context_t;

// 全局环境变量
nf_context_t	nf_context	= {0};			

/*
 * 包回调函数
 */
int cb(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg
		, struct nfq_data *nfa, void *data)
{
	u_int32_t							id				= 0;
	struct nfqnl_msg_packet_hdr*		ph;
	unsigned char*						pdata			= NULL;
	int									pdata_len		= 0;
	struct iphdr*						iphdrp			= NULL;
	struct tcphdr*						tcphdrp;
	int									eth				= 0;
	char								szShow[32]		= {0};
	char*								pHost			= NULL;
	struct nfqnl_msg_packet_hw*			phw;
	MYSQL*								mysql;
	int									fd;

	ph = nfq_get_msg_packet_hdr(nfa);
	if( ph )
	{
		id = ntohl(ph->packet_id);
	}

	pdata_len = nfq_get_payload(nfa, (char**)&pdata);
	if( -1 == pdata_len )
	{
		pdata_len = 0;
	}

	iphdrp = (struct iphdr*)pdata;
	if( NULL == iphdrp )
	{
		printf("error: nfq_set_verdict_mark\n");
		goto cb_end;
	}

	if( IPPROTO_TCP != iphdrp->protocol )
	{
		goto cb_end;
	}
	// 获取来源网卡
	eth = nfq_get_physoutdev(nfa);
	if(nf_context.outeth != eth)
	{
		goto cb_end;
	}
	// 判断数据标志
	tcphdrp	= (struct tcphdr*)((u_int8_t*)iphdrp + (iphdrp->ihl << 2));
	// 分析连接协议
	strncpy(szShow, (char*)tcphdrp + (tcphdrp->doff << 2), 16);
	if( strncasecmp(szShow, "GET\x20", 4) != 0 )
	{
		goto cb_end;
	}
	// 打印MAC
	phw = nfq_get_packet_hw(nfa);
//	printf("%02hx-%02hx-%02hx-%02hx-%02hx-%02hx ",
//			phw->hw_addr[0], phw->hw_addr[1], phw->hw_addr[2],
//			phw->hw_addr[3], phw->hw_addr[4], phw->hw_addr[5]);
	// 获取上下文
	if(false == thread_context_get(&mysql, &fd))
	{
		goto cb_drop;
	}
	// 验证MAC
	if(false == checkurl(mysql, nf_context.sendfd, fd, phw->hw_addr, pdata))
	{
		goto cb_drop;
	}
	// 记录MAC	
	goto cb_end;
cb_drop:
	return nfq_set_verdict_mark(qh, id, NF_DROP, 1, 0, NULL);
cb_end:
	return nfq_set_verdict_mark(qh, id, NF_REPEAT/*NF_ACCEPT*/, 1, (u_int32_t)pdata_len, pdata);
}
/*
 * 监听线程
 */
void* thread_recv(void* pp)
{
	int			rv;
	char		buf[4096];

	while((rv = recv(nf_context.fd, buf, sizeof(buf), 0)) && rv >= 0)
	{
		nfq_handle_packet(nf_context.handle, buf, rv);
		memset(buf, 0, sizeof(buf));
	}

	syslog(LOG_ERR, "thread_recv recv failed: %s", strerror(errno));
	return NULL;
}
/*
 * 测试主函数
 */
int nfq_start(int ethout)
{
	struct nfq_q_handle*	qh;
	int						qid				= 8603;
	int						i;

	// 设置监控的网卡编号
	nf_context.outeth = ethout;
	// 打开监控驱动
	nf_context.handle = nfq_open();
	if( !nf_context.handle )
	{
		syslog(LOG_ERR, "nfq_open failed: %s", strerror(errno));
		exit(1);
	}
	// 取消原绑定的端口
	if( nfq_unbind_pf(nf_context.handle, AF_INET) < 0 )
	{
		syslog(LOG_ERR, "nfq_unbind_pf failed: %s", strerror(errno));
		exit(1);
	}
	// 重新绑定端口
	if( nfq_bind_pf(nf_context.handle, AF_INET) < 0 )
	{
		syslog(LOG_ERR, "nfq_bind_pf failed: %s", strerror(errno));
		exit(1);
	}
	// 设置监听回调函数
	qh = nfq_create_queue(nf_context.handle, qid, &cb, NULL);
	if( !qh )
	{
		syslog(LOG_ERR, "nfq_create_queue failed: %s", strerror(errno));
		exit(1);
	}
	// 设置COPY模式
	if(nfq_set_mode(qh, NFQNL_COPY_PACKET, 0xffff) < 0)
	{
		syslog(LOG_ERR, "nfq_set_mode failed: %s", strerror(errno));
		exit(1);
	}
	// 获取接收文件描述符
	nf_context.fd = nfq_fd(nf_context.handle);
	// 创建接收线程
	thread_context_init(CHECKURL_THREAD);
	nf_context.sendfd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);
	if(nf_context.sendfd < 0)
	{
		syslog(LOG_ERR, "socket SOCK_RAW failed: %s", strerror(errno));
	}
	i = 1;
	if( setsockopt(nf_context.sendfd, IPPROTO_IP, IP_HDRINCL, &i, sizeof(i)) )
	{
		syslog(LOG_ERR, "setsockopt IP_HDRINCL failed: %s", strerror(errno));
		exit(1);
	}

	for(i = 1; i < CHECKURL_THREAD; i++)
	{
		pthread_t		tid;
		int				err;

		err = pthread_create(&tid, NULL, thread_recv, NULL);
		if(0 != err)
		{
			syslog(LOG_ERR, "pthread_create %d failed: %s", i, strerror(err));
		}
	}
	
	thread_recv(NULL);
	// 接收完成， 退出
	syslog(LOG_ERR, "!!!error: recv %s\n", (char *)strerror(errno));
	nfq_destroy_queue(qh);

	nfq_close(nf_context.handle);
	exit(-1);
	return 0;
}

